﻿using System.Collections.Generic;
using System.Linq;

namespace Gumini.JulaDB.Engine
{
    /// <summary>
    /// A collection of row values used for grouping by the GROUP BY statement.
    /// Objects of this class are used as dictionary keys.
    /// </summary>
    internal class CompositeKey<T>
    {
        private readonly List<T> _values;

        public CompositeKey(IEnumerable<T> values)
        {
            _values = values.ToList();
        }

        public override string ToString()
        {
            return string.Join(", ", _values.Select(v => v.ToString()).ToArray());
        }

        public T this[int index]
        {
            get { return _values[index]; }
        }

        public class EqualityComparer : IEqualityComparer<CompositeKey<T>>
        {
            private readonly bool _emptyGroupsAreEqual;

            /// <param name="emptyGroupsAreEqual">True if there is no GROUP BY statement but aggregate expressions are used.</param>
            public EqualityComparer(bool emptyGroupsAreEqual)
            {
                _emptyGroupsAreEqual = emptyGroupsAreEqual;
            }

            public bool Equals(CompositeKey<T> x, CompositeKey<T> y)
            {
                // if the grouping key is empty, we treat the groups as separate
                // except for the case when there is no GROUP BY statement but aggregate expressions are used
                if (!x._values.Any() || !y._values.Any())
                    return _emptyGroupsAreEqual;
                if (x._values.Count != y._values.Count)
                    return false;
                for (int i = 0; i < x._values.Count; i++)
                {
                    if (!x._values[i].Equals(y._values[i]))
                        return false;
                }
                return true;
            }

            public int GetHashCode(CompositeKey<T> x)
            {
                if (x._values == null || !x._values.Any())
                    return base.GetHashCode();
                var result = x._values.
                    Select(v => v.GetHashCode()).
                    Aggregate((acc, value) => acc * 7 + value);
                return result;
            }
        }
    }
}
